package es.deusto.ingenieria.ssdd.bitTorrent.util;

/*
 * Copyright 2006 Robert Sterling Moore II This computer program is free
 * software; you can redistribute it and/or modify it under the terms of the GNU
 * General Public License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version. This
 * computer program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
 * details. You should have received a copy of the GNU General Public License
 * along with this computer program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.BitSet;
import java.util.Random;

/**
 * @author Robert S. Moore II
 */
public class ToolKit {

	private static final String ALLOWED_CHARS = "$-_!";
	private static char[] HEX_CHARS = { '0', '1', '2', '3', '4', '5', '6', '7',
			'8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };

	public static byte[] intToBigEndianBytes(int int_value, byte[] bytes,
			int offset) {
		bytes[3 + offset] = (byte) int_value;
		int_value >>>= 8;
		bytes[2 + offset] = (byte) int_value;
		int_value >>>= 8;
		bytes[1 + offset] = (byte) int_value;
		int_value >>>= 8;
		bytes[0 + offset] = (byte) int_value;
		return bytes;
	}

	public static int bigEndianBytesToInt(byte[] bytes, int i) {
		return ((bytes[0 + i] & 0xFF) << 24) + ((bytes[1 + i] & 0xFF) << 16)
				+ ((bytes[2 + i] & 0xFF) << 8) + (bytes[3 + i] & 0xFF);
	}

	/**
	 * Converts a byte[] into a BitSet.
	 * 
	 * @author Chris Lauderdale
	 * @param bytes
	 *            A <code>byte[]</code> representing the bits in
	 *            <code>bytes</code>.
	 * @return A BitSet object representing the input byte[].
	 */
	public static BitSet bytesToBitSet(byte[] bytes) {
		final BitSet rv = new BitSet();

		bytesToBitSet(bytes, rv);

		return rv;
	}

	/**
	 * Converts a byte[] into a BitSet.
	 * 
	 * @author Chris Lauderdale
	 * @param bytes
	 *            A <code>byte[]</code> representing the bits in
	 *            <code>bytes</code>.
	 * @param bits
	 *            The {@link BitSet} to fill.
	 */
	public static void bytesToBitSet(byte[] bytes, final BitSet bits) {
		int i, j, k;

		bits.clear();

		for (i = k = 0; i < bytes.length; i++) {
			for (j = 0x80; j != 0; j >>>= 1, k++) {
				if ((bytes[i] & j) != 0) {
					bits.set(k);
				}
			}
		}
	}

	/**
	 * Converts a <code>BitSet</code> into a <code>byte[]</code>.
	 * 
	 * @author Chris Lauderdale
	 * @param bits
	 *            A {@link BitSet}.
	 * @param bytes
	 *            The <code>byte</code> array to fill.
	 */
	public static void bitSetToBytes(BitSet bits, byte[] bytes) {
		int i;

		java.util.Arrays.fill(bytes, (byte) 0);

		for (i = bits.nextSetBit(0); i >= 0; i = bits.nextSetBit(i + 1)) {
			bytes[i >>> 3] |= 0x80 >>> (i & 7);
		}
	}

	/**
	 * Converts a <code>BitSet</code> into a <code>byte[]</code>.
	 * 
	 * @author Chris Lauderdale
	 * @param bits
	 *            A <code>BitSet</code>.
	 * @return A <code>byte[]</code> containing the same bits stored in
	 *         <code>bits</code>.
	 */
	public static byte[] bitSetToBytes(BitSet bits) {
		final byte[] bytes = new byte[(bits.size() + 7) >>> 3];

		bitSetToBytes(bits, bytes);

		return bytes;
	}

	static void dumpBytes(String s, final byte[] array) {
		if (s != null) {
			System.out.print(s);
		}

		for (int i = 0; i < array.length; i++) {
			if ((i & 15) == 0) {
				System.out.print("\n@" + Integer.toString(i, 16) + ':');
			}

			System.out.print(" " + Integer.toString(255 & (int) array[i], 16));
		}

		System.out.println();
	}

	public static String makeHTTPEscaped(String str) {
		final int l = str.length();
		char c;

		for (int i = 0; i < l; i++) {
			c = str.charAt(i);

			if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')
					|| (c >= '0' && c <= '9') || ALLOWED_CHARS.indexOf(c) >= 0) {
				continue;
			}

			final StringBuffer rv = new StringBuffer(str.substring(0, i));

			c &= 0xFF;
			rv.append('%').append(HEX_CHARS[c >>> 4]).append(HEX_CHARS[c & 15]);

			for (++i; i < l; i++) {
				c = str.charAt(i);

				if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')
						|| (c >= '0' && c <= '9')
						|| ALLOWED_CHARS.indexOf(c) >= 0) {
					rv.append(c);
				} else {
					c &= 0xFF;
					rv.append('%').append(HEX_CHARS[c >>> 4])
							.append(HEX_CHARS[c & 15]);
				}
			}

			return rv.toString();
		}

		return str;
	}

	public static byte[] fileToByteArray(String filename) {
		byte[] result = null;

		if (filename != null) {
			try (FileInputStream fis = new FileInputStream(filename);
					ByteArrayOutputStream bos = new ByteArrayOutputStream()) {
				byte[] buf = new byte[1024];

				for (int readNum; (readNum = fis.read(buf)) != -1;) {
					bos.write(buf, 0, readNum);
				}

				result = bos.toByteArray();
			} catch (IOException e) {
				System.err.println("# fileToByteArray(): " + e.getMessage());
			}
		}

		return result;
	}

	public static byte[] generateSHA1Hash(byte[] bytes) {
		try {
			byte[] hash = new byte[20];
			MessageDigest sha = MessageDigest.getInstance("SHA-1");
			hash = sha.digest(bytes);

			return hash;
		} catch (NoSuchAlgorithmException e) {
			System.err.println("'SHA-1' is not a valid algorithm name.");
		}

		return null;
	}

	public static String generatePeerId() {
		StringBuffer bufferID = new StringBuffer();
		bufferID.append("-");
		bufferID.append("SSDD01");
		bufferID.append("-");
		Random random = new Random();

		for (int i = 0; i < 12; i++) {
			bufferID.append(random.nextInt(9));
		}

		return bufferID.toString();
	}
}